package com.paas.demo.netty.websocket;

import com.paas.demo.netty.Decoder4LoggingOnly;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelOption;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.http.HttpObjectAggregator;
import io.netty.handler.codec.http.HttpServerCodec;
import io.netty.handler.codec.http.websocketx.WebSocketServerProtocolHandler;
import io.netty.handler.stream.ChunkedWriteHandler;
import io.netty.handler.timeout.IdleStateHandler;
import io.netty.util.concurrent.Future;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.concurrent.TimeUnit;

/**
 * @ClassName : NettyServer 
 * @Description : TODO
 * @author : pengjie
 * @email  : 627799251@qq.com
 * @version  
 * @Date ： 2018年12月26日 上午10:17:31
*/

public class NettyWebSocketServer {

	private Logger log = LoggerFactory.getLogger(getClass());
	private volatile boolean isRunning = false;

	private EventLoopGroup bossGroup = null;
	private EventLoopGroup workerGroup = null;
	private static int port;

	private static int DEFAULT_PORT = 9001;

	public NettyWebSocketServer() {
		this(DEFAULT_PORT);
	}

	public NettyWebSocketServer(int port) {
		NettyWebSocketServer.port = port;
	}

	private void bind() throws Exception {
		this.bossGroup = new NioEventLoopGroup();
		this.workerGroup = new NioEventLoopGroup();

		ServerBootstrap serverBootstrap = new ServerBootstrap();
		serverBootstrap.group(bossGroup, workerGroup)//
				.channel(NioServerSocketChannel.class) //
				.childHandler(new ChannelInitializer<SocketChannel>() { //
					@Override
					public void initChannel(SocketChannel ch) throws Exception {
						ch.pipeline().addLast("idleStateHandler", new IdleStateHandler(30, 0, 0, TimeUnit.MINUTES));
						//关闭日志
						ch.pipeline().addLast(new Decoder4LoggingOnly());
						//HttpServerCodec: 针对http协议进行编解码
						ch.pipeline().addLast("httpServerCodec", new HttpServerCodec());
						//ChunkedWriteHandler分块写处理，文件过大会将内存撑爆
						ch.pipeline().addLast("chunkedWriteHandler", new ChunkedWriteHandler());
						/**
						 * 作用是将一个Http的消息组装成一个完成的HttpRequest或者HttpResponse，那么具体的是什么
						 * 取决于是请求还是响应, 该Handler必须放在HttpServerCodec后的后面
						 */
						ch.pipeline().addLast("httpObjectAggregator", new HttpObjectAggregator(8192));
						//用于处理websocket, /ws为访问websocket时的uri
						ch.pipeline().addLast("webSocketServerProtocolHandler", new WebSocketServerProtocolHandler("/ws"));
						ch.pipeline().addLast("myWebSocketHandler", new MyWebSocketHandler());
					}
				}).option(ChannelOption.SO_BACKLOG, 128) //
				.childOption(ChannelOption.SO_KEEPALIVE, true);

		this.log.info("TCP服务启动完毕,port={}", NettyWebSocketServer.port);
		ChannelFuture channelFuture = serverBootstrap.bind(NettyWebSocketServer.port).sync();


		channelFuture.channel().closeFuture().sync();
	}

	public synchronized void startServer() {
		if (this.isRunning) {
			throw new IllegalStateException(this.getName() + " is already started .");
		}
		this.isRunning = true;

		new Thread(() -> {
			try {
				this.bind();
			} catch (Exception e) {
				this.log.info("TCP服务启动出错:{}", e.getMessage());
				e.printStackTrace();
			}
		}, this.getName()).start();
	}

	public synchronized void stopServer() {
		if (!this.isRunning) {
			throw new IllegalStateException(this.getName() + " is not yet started .");
		}
		this.isRunning = false;

		try {
			Future<?> future = this.workerGroup.shutdownGracefully().await();
			if (!future.isSuccess()) {
				log.error("workerGroup 无法正常停止:{}", future.cause());
			}

			future = this.bossGroup.shutdownGracefully().await();
			if (!future.isSuccess()) {
				log.error("bossGroup 无法正常停止:{}", future.cause());
			}
		} catch (InterruptedException e) {
			e.printStackTrace();
		}

		this.log.info("TCP服务已经停止...");
	}

	private String getName() {
		return "TCP-Server";
	}

	public static void main(String[] args) {
		NettyWebSocketServer server = new NettyWebSocketServer(8088);
		server.startServer();
	}
}
